<?php
/**
 * mysql数据库驱动类
 *
 * @author Mo yi <root@imoi.cn>
 * @link   http://www.imoi.cn
 * @copyright Copyright &copy; 2010-2012 DuoLam Software LLC
 * @license http://www.imoi.cn/license/
 * @version $Id: dpMysqlDriver.php 2799 2012-04-10 09:31:13Z Mo.yi $
 * @package db.drivers
 * @since 1.0
 */

class dpMysqlDriver extends dpComponent implements IModel
{

	protected $queryNum = 0;

	public    $connect  = array();

	protected $selfConnect;

	protected $querySql = array();

	protected $exeTime = array();

	protected $_stmtSql;

	protected $prefix;

	protected $sourse;

	/**
	 * MYSQL驱动初始化，连接数据库
	 * @param string $name   主从库连接名
	 * @param array  $config 数据库配置信息
	 */
	public function __construct($name,$config)
	{
		if (!isset($this->connect[$name])) {
			$this->connect[$name] = @mysql_connect($config['host'],$config['username'],$config['passwd'], isset($config['newlink']) ? true : false);
			(!$this->connect[$name]) && $this->halt('连接不到Mysql服务器');
			$this->selfConnect    = &$this->connect[$name];
			mysql_select_db($config['dbname'],$this->connect[$name]) or $this->halt('连接不到数据库,'.$config['dbname']);
			$this->query("SET NAMES ".$config['charset']);
			$this->prefix = $config['prefix'];
		}
	}

	public function __call($name,$param)
	{
		method_exists($this, $name) or $this->halt('调用的 '.$name.' 方法不存在');
	}

	/**
	 * 执行SQL命令方法，如果是insert、delete、update操作则调用execute方法
	 * 非查询SQL命令返回影响记录的行数
	 * 是查询SQL命令返回资源句柄
	 * 每次调用该方法，统计查询数+1
	 * @param string $sql  SQL语句
	 * @return resource or int  资源类型或者影响记录行数的结果
	 */
	public function query($sql)
	{
		$this->queryNum++;
		if (!preg_match('/SELECT|SHOW/i',$sql))
			return $this->execute($sql);
		else {
			$this->querySql[] = $this->getDealTablePrefix($sql);
			$start = microtime(true);
			$sourse = @mysql_query($this->getDealTablePrefix($sql),$this->selfConnect);
			$this->exeTime[$this->getDealTablePrefix($sql)] = substr(microtime(true) - $start, 0, 8);
			if (!$sourse) $this->halt('SQL查询错误',$sql);

			$this->sourse = &$sourse;
			return $sourse;
		}
	}

	/**
	 * 返回页面查询的SQL语句
	 */
	public function getQuerySql()
	{
		return $this->querySql;
	}

	/**
	 * 返回每条SQL执行时间
	 */
	public function getSqlExeTime()
	{
		return $this->exeTime;
	}

	/**
	 * 开启事务
	 */
	public function startAffairs()
	{
		$this->query('BEGIN');
		return true;
	}

	/**
	 * 提交事务给数据库，并开启自动提交模式
	 */
	public function submitAffairs()
	{
		$this->query('COMMIN');
		return true;
	}

	/**
	 * 事务回滚，并开启自动提交模式
	 */
	public function rollbackAffairs()
	{
		$this->query('ROLLBACK');
		return true;
	}

	/**
	 * 执行非SELECT操作接口，返回影响记录的行数
	 * @param string $sql
	 * @return int
	 */
	public function execute($sql)
	{
		$sourse = @mysql_query($this->getDealTablePrefix($sql),$this->selfConnect);

		if (!$sourse) $this->halt('SQL执行错误',$sql);

		if (preg_match('/INSERT/i',$sql))
			return $this->lastId();
		else
			return mysql_affected_rows($this->selfConnect);
	}

	/**
	 * 返回最后insert之后的ID
	 */
	public function lastId()
	{
		return mysql_insert_id($this->selfConnect);
	}

	/**
	 * 获取结果集
	 * @param mixed $sourse query方法返回的资源
	 * @param mixed $mode   返回数据模式
	 */
	public function fetch($sourse,$mode=MYSQL_ASSOC)
	{
		return $this->unEscape(@mysql_fetch_array($sourse,$mode));
	}

	/** 
	 * 获取表中的一行数据
	 * @param string $sql    SQL语句
	 * @param int    $expire 缓存有效期
	 * @param mixed  $mode   返回类型
	 * @return mixed 查询的数据集
	 */
	public function find($sql,$expire=-1,$mode=MYSQL_ASSOC)
	{
		if (!preg_match('/limit/i', $sql)) {
			$sql .= ' LIMIT 1';
		}
		if ($expire != -1) {
			$cache   = $this->getCache();
			$content = $cache->get($sql);

			if ($content)
				return $content;
			else {
				$sourse = $this->query($sql);
				$result = $this->fetch($sourse,$mode);
				$cache -> set($sql,$result,$expire);
				return $result;
			}
		} else {
			$sourse = $this->query($sql);
			return $this->fetch($sourse,$mode);
		}
	}

	/** 
	 * 获取表中的多行数据
	 * @param string $sql    SQL语句
	 * @param int    $expire 缓存有效期
	 * @param mixed  $mode   返回类型
	 * @return mixed 查询的数据集
	 */
	public function finds($sql,$expire=-1,$mode=MYSQL_ASSOC)
	{
		$data = array();

		if ($expire != -1) {
			$cache   = $this->getCache();
			$content = $cache->get($sql.'_all');
			if ($content)
				return $content;
			else {
				$sourse = $this->query($sql);
				while($result = $this->fetch($sourse,$mode))
					$data[] = $result;
				$cache -> set($sql.'_all',$data,$expire);
				return $data;
			}
		} else {
			$sourse = $this->query($sql);

			while ($result = $this->fetch($sourse,$mode)) $data[] = $result;

			return $data;
		}
	}

	/**
	 * Mysqli驱动支持SQL预处理
	 * @param string $sql   带占位符的SQL语句
	 * @return object       类本身
	 */
	public function prepare($sql)
	{
		$this->_stmtSql = $this->getDealTablePrefix($sql);
		return $this;
	}

	/**
	 * 预处理绑定参数
	 * @param array 参数列表
	 * @return object 类本身
	 */
	public function bindParam($array)
	{
		if (is_array($array)) {
			foreach ($array as $k => $v) $this->_stmtSql = str_replace($k,$this->escape($v),$this->_stmtSql);
			return $this;
		}
		return false;
	}

	/**
	 * INSERT UPDATE等语句通过此接口进行预处理查询
	 * @return int 返回影响的行数或者最后插入的ID
	 */
	public function stmtQuery()
	{
		$this->queryNum++;
		if (!preg_match('/SELECT|SHOW/i',$this->_stmtSql)) {
			return $this->execute($this->_stmtSql);
		}
	}

	/**
	 * 通过预处理获得一行结果集
	 * @param  int   $expire 缓存过期时间
	 * @return array 返回一行数据的结果集
	 */
	public function stmtFind($expire=-1)
	{
		return $this->find($this->_stmtSql,$expire);
	}

	/**
	 * 通过预处理获取多个结果集
	 * @param  int   $expire 缓存过期时间
	 * @return array 返回多行数据的结果集
	 */
	public function stmtFinds($expire=-1)
	{
		return $this->finds($this->_stmtSql);
	}

	/**
	 * 返回统计行数
	 * 如果是sql语句的话，则进行查询，返回统计行数
	 * 如果是资源的话，直接返回统计行数
	 * @param string or resource  $sql SQL语句或资源
	 * @return int
	 */
	public function count($sql=null)
	{
		if (preg_match('/SELECT/i',$sql)) {
			$sourse = $this->query($sql);
			return @mysql_num_rows($sourse);
		} else
			return @mysql_num_rows($this->sourse);
	}

	/**
	 * 对数据进行转义处理
	 * @param string or array $value 
	 * @return string or array
	 */
	public function escape($value)
	{
		if (is_array($value)) { 
		   return array_map(array($this, 'escape'), $value);
		} else {

		   if (get_magic_quotes_gpc()) {
			   $value = stripslashes($value);
		   }

		   if ( !is_numeric($value) || $value[0] == '0' ) {
			   $value = '\''.mysql_real_escape_string(trim($value,"'"), $this->selfConnect).'\'';
		   }

		   return $value;
		}
	}

	/**
	 * 对转义后的数据进行复原
	 * @param string or array $value
	 * @return string or array
	 */
	public function unEscape($value)
	{
		if (is_array($value)) {
			$data = array();

			foreach ($value as $k => $v) {
				if(is_array($v)) {
					foreach($v as $k1 =>$v1) $data[$k][$k1] = $this->removeQuote(stripslashes($v1));
				} else
					$data[$k] = $this->removeQuote(stripslashes($v));
			}

			return $data;
		} else
			return $this->removeQuote(stripslashes($value));
	}

	/**
	 * 去掉前后单引号
	 */
	public function removeQuote($str)
	{
		if (preg_match("/^\'/",$str))
            $str = substr($str, 1, strlen($str) - 1);

		if (preg_match("/\'$/",$str))
			$str = substr($str, 0, strlen($str) - 1);

		$str = str_replace(array("\\\'","\\&quot;"), array("'",'"'), $str);
		return $str;
	}

	/**
	 * 释放查询的内存
	 * @param resource $result
	 */
	public function free($result)
	{
		return mysql_free_result($result);
	}

	/**
	 * 返回当前页进行了多少次查询
	 */
	public function getQueryNum()
	{
		return $this->queryNum;
	}

	/**
	 * 获取对SQL语句表前缀处理后的SQL
	 * @param string $sql   SQL语句
	 * @return string 处理后的SQL语句
	 */
	public function getDealTablePrefix($sql)
	{
		return preg_replace('/\{(\w+)\}/i', $this->prefix.'\\1', $sql);
	}

	/**
	 * 获取缓存句柄
	 */
	public function getCache()
	{
		return D::app()->cache();
	}

	/**
	 * 获取当前连接数据库的句柄
	 * @return resource
	 */
	public function getConnect()
	{
		return $this->selfConnect;
	}

	/**
	 * 错误控制台
	 * @param string $msg  错误信息
	 * @param string $sql  错误的SQL语句
	 */
	public function halt($msg, $sql=null)
	{
		$info = array(
			'sql'       => $this->getDealTablePrefix($sql),
			'errorInfo' => mysql_error(),
			'errorCode' => mysql_errno(),
		);
		dpException::error($msg.'，dpMysqlDriver',true,$info);
	}

	/**
	 * 关闭连接数据库
	 * 关闭所有连接数据库的句柄，并重置初始值
	 */
	public function close()
	{
		static $sourse;

		foreach ($this->connect as $k => $v) {
			if($sourse == $v)
				continue;
			else {
				$sourse = $v;
				mysql_close($v);
			}
		}

		if (!empty($this->sourse)) $this->free($this->sourse);
		
		$this->selfConnect = null;
		$this->connect     = array();
	}

	/**
	 * 析构方法，在释放内存的时候，执行关闭数据库连接
	 */
	public function __destruct()
	{
		$this->close();
	}

}
?>